package com.cloud.easy.im.utils;

import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 高并发场景下，currentTimeMillis()方法获取时间戳会出现一下问题：
 * 1。为负数
 * 2。效率比较差，甚至比创建一个对象的耗时还要多，因为从hotspot源码中我们可以看到回调用gettimeofday()的方法
 * 此方法会从用户态切换到内核态。而且系统只有一个全局时钟，反复调用会出现严重竞争问题
 *
 * @author wangjian
 * @version 0.1.0
 * @create 2020年10月10日11:14 上午
 */
public class SystemClock {

    /**
     * 线程池头名称
     */
    private static final String THRAD_NAME = "system.clock";

    private volatile long now;

    private static class SingletonHolder {
        private static final SystemClock INSTANCE = new SystemClock();
    }

    public static SystemClock getInstance() {
        return SingletonHolder.INSTANCE;
    }

    private SystemClock() {
        this.now = System.currentTimeMillis();
        scheduleTick();
    }

    /**
     * 通过定时任务单线程获取本地系统事件
     */
    private void scheduleTick() {
        new ScheduledThreadPoolExecutor(1,
                new BasicThreadFactory.Builder()
                        .namingPattern(THRAD_NAME)
                        .daemon(true)
                        .build())
                .scheduleAtFixedRate(() -> now = System.currentTimeMillis(), 1, 1, TimeUnit.MILLISECONDS);
    }

    public long getNow() {
        return now;
    }
}
